/*
 * FileCopyConsoleHelper.java
 *
 * Created on August 21, 2007, 9:41 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package filecopier.console.copy;

import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.SimpleTimeZone;
import org.apache.log4j.Logger;
import filecopier.Singleton;
import filecopier.options.Options;
import filecopier.options.RenameFmtString;
import filecopier.script.Script;
import filecopier.script.ScriptEntry;
import filecopier.script.RestoreScriptEntry;
import custom.util.FileCopyHelper;
import custom.util.FileCopyCancelException;

/**
 *
 * @author david
 */
public class FileCopyConsoleHelper implements FileCopyHelper {

    private boolean update = false;
    private boolean confirmOverwrite = false;
    private boolean useGMT = false;
    private RenameFmtString renameFmtString = null;
    private Options.RenameType renameType = null;
    private Script restoreScript = null;
    private Logger logger = null;
    private int restoreCount = 0;

    /** Creates a new instance of FileCopyConsoleHelper */
    public FileCopyConsoleHelper(final Script restoreScript) {
        init(restoreScript);
    }

    public void init(final Script restoreScript) {
        Options options = Singleton.getInstance().getOptions();
        this.useGMT = options.getUseGMT();
        this.update = options.getUpdateOnly();
        this.renameType = options.getRenameType();
        this.confirmOverwrite = options.promptForOverwrite();
        this.renameFmtString = options.getRenameFmtString();
        this.restoreScript = restoreScript;
        this.logger = Singleton.getInstance().getFileConsoleLogger();
    }

    public void handleException(final java.io.IOException e) {
        logger.error("IOException: ", e);
    }

    public boolean handleExistingFile(final File existingFile, final File sourceFile) throws FileCopyCancelException {
        boolean status = true;
        /* If update is true, we are supposed to make sure that the destination file is older than the source file
        before copying it. If the files are directories, however, I do not want to perform this check. The check
        will be performed for each file in the directory. */
        if ((update == true) && (!existingFile.isDirectory() && !sourceFile.isDirectory()) &&
                (existingFile.lastModified() >= sourceFile.lastModified())) {
            logger.info("\"" + existingFile + "\" is not older than " + sourceFile + "\". Skipping file.");
            status = false;
        } else {
            if (renameType == Options.RenameType.Skip) {
                // Rename type is skip, which means do not copy any existing files.
                logger.info("\"" + existingFile + "\" already exists. Skipping file.");
                status = false;
            } else if (renameType == Options.RenameType.Overwrite) {
                status = handleOverwrite(existingFile);
            } else if (renameType == Options.RenameType.RenameAuto) {
                status = handleRenameAuto(existingFile);
            } else {
                status = handleRenameManual(existingFile);
            }
        }
        return status;
    }

    private boolean handleOverwrite(final File existingFile) throws FileCopyCancelException {
        boolean status = false;

        if (confirmOverwrite) {
            boolean wrongAnswer = false;

            // Have the user confirm the overwrite before doing so with an option pane.
            System.out.println(existingFile + " already exists. Are you sure you want to overwrite?(yes or y, yes to all or ya, no or n, no to all or na (case insensitive))");

            do {
                String answer = System.console().readLine();
                if (answer.matches("[Yy]([Ee][Ss])?")) {
                    status = true;
                    wrongAnswer = false;
                } else if (answer.matches("[Yy]([Ee][Ss] [Tt][Oo] )?[Aa]([Ll][Ll])?")) {
                    // User said yes to all, so don't confirm subsequent overwrites.
                    confirmOverwrite = false;
                    status = true;
                    wrongAnswer = false;
                } else if (answer.matches("[Nn][Oo]?")) {
                    status = false;
                    wrongAnswer = false;
                } else if (answer.matches("[Nn]([Oo] [Tt][Oo] )?[Aa]([Ll][Ll])?")) {
                    throw new FileCopyCancelException("Subsequent copy operations aborted");
                } else {
                    wrongAnswer = true;
                    System.out.println(answer + " is an invalid answer. Valid answers are: (yes or y, yes to all or ya, no or n, no to all or na (case insensitive)) Do you want to overwrite?");
                }
            } while (wrongAnswer == true);
        } else {
            // No need to confirm overwrite, so return true.
            status = true;
        }

        return status;
    }

    private boolean handleRenameAuto(final File existingFile) {
        boolean status = true;
        try {
            createNewFileWithLastModified(existingFile);
        } catch (IOException e) {
            status = false;
            handleException(e);
        }

        return true;
    }

    private boolean handleRenameManual(final File existingFile) throws FileCopyCancelException {
        boolean status = true;
        try {
            status = promptForNewName(existingFile);
        } catch (IOException e) {
            status = false;
            handleException(e);
        }

        if (status == false) {
            // Log the fact that renaming failed to the log file.
            Singleton.getInstance().getFileLogger().error("Renaming of \"" + existingFile + "\" failed.");
            System.out.println("Renaming of \"" + existingFile + "\" failed. Do you want to abort subsequent copy operations(yes or y, or no n (case insensitive))");
            boolean wrongAnswer = false;

            do {
                String answer = System.console().readLine();
                if (answer.matches("[Yy]([Ee][Ss])?")) {
                    throw new FileCopyCancelException("Subsequent copy operations aborted");
                } else if (answer.matches("[Nn][Oo]?")) {
                    wrongAnswer = false;
                } else {
                    wrongAnswer = true;
                    System.out.println(answer + " is an invalid answer. Valid answers are: (yes or y, or no n (case insensitive)) Do you want to abort subsequent copy operations?");
                }
            } while (wrongAnswer == true);
        }

        return status;
    }

    private boolean promptForNewName(final File toFile) throws IOException, FileCopyCancelException {
        boolean status = false;
        File newToFile = new File(toFile.getParent(), createNewName(toFile.getName(), toFile.lastModified()));
        int idx = 1;
        while (newToFile.exists() && idx <= Integer.MAX_VALUE) {
            String newName = newToFile.getName();
            newName = incrementNewFileName(newName, idx++);
            newToFile = new File(newToFile.getParent(), newName);
        }

        System.out.println();
        System.out.println("\"" + toFile + "\" already exists. To what do you want to rename the file? (To rename the file to \"" +
                newToFile.getName() + "\" press Enter)");
        String answer = System.console().readLine();

        if (!answer.equals("")) {
            newToFile = new File(newToFile.getParent() + System.getProperty("file.separator"), answer);
        }
        if (!toFile.renameTo(newToFile)) {
            throw new IOException("File NOT renamed from \"" + toFile + "\" to \"" + newToFile + "\"");
        }
        logger.info("File renamed from \"" + toFile + "\" to \"" + newToFile + "\"");
        status = true;

        // Write info to the restore script file.
        if (restoreScript != null) {
            ScriptEntry rse = new RestoreScriptEntry(newToFile, toFile);
            restoreScript.addScriptEntry(rse);
            restoreCount++;
        }

        return status;
    }

    public boolean handleFileInUse(final File existingFile) {
        boolean status = false;

        if (renameType == Options.RenameType.Overwrite) {
            logger.info("Overwrite failed because \"" + existingFile + "\" is in use by another process. Attempting rename.");
            status = handleRenameAuto(existingFile);
        }
        return true;
    }

    public void reportCopyStatus(final String msg, boolean status) {
        if (status) {
            logger.info(msg);
        } else {
            logger.warn(msg);
        }
    }

    private void createNewFileWithLastModified(final File toFile) throws IOException {
        File newToFile = new File(toFile.getParent(), createNewName(toFile.getName(), toFile.lastModified()));
        int idx = 1;
        while (newToFile.exists()) {
            String newName = newToFile.getName();
            newName = incrementNewFileName(newName, idx++);
            newToFile = new File(newToFile.getParent(), newName);
        }

        if (!toFile.renameTo(newToFile)) {
            throw new IOException("File NOT renamed from \"" +
                    toFile + "\" to \"" + newToFile + "\"");
        }
        logger.info("File renamed from \"" + toFile + "\" to \"" + newToFile + "\"");
        // Write info to the restore script file.
        if (restoreScript != null) {
            ScriptEntry rse = new RestoreScriptEntry(newToFile, toFile);
            restoreScript.addScriptEntry(rse);
            restoreCount++;
        }
    }

    private String createNewName(final String fileName, final long lastModMillis) {
        String nameOnly, newFileName, ext;
        // See if this file has an extension
        int idx = fileName.lastIndexOf('.');
        if (idx > 0) {
            // File has an extension, so put the name and extension in separate Strings.
            nameOnly = fileName.substring(0, idx);
            ext = fileName.substring(idx);
        } else {
            /* File has no extension, so set sNameOnly to point to the whole name and set sExt to point to
            an empty String. */
            nameOnly = fileName;
            ext = "";
        }

        // Get the date that the to file was last modified and put that value in a Calendar object.
        Calendar cal = null;
        if (useGMT) {
            cal = Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
        } else {
            cal = Calendar.getInstance();
        }
        cal.setTimeInMillis(lastModMillis);
        renameFmtString.validateRenameFmtString(cal);

        // Build the new file name with the formatted last modified date string appended to it.
        newFileName = nameOnly + renameFmtString.getPreviewRenameFmtString() + ext;

        return newFileName;
    }

    private String incrementNewFileName(String name, final int idx) {
        String nameOnly, ext;
        // See if this file has an extension
        int extIdx = name.lastIndexOf('.');
        if (extIdx > 0) {
            // File has an extension, so put the name and extension in separate Strings.
            nameOnly = name.substring(0, extIdx);
            ext = name.substring(extIdx);
        } else {
            // File has no extension, so set sNameOnly to point to the whole name and set sExt to point to
            // an empty String.
            nameOnly = name;
            ext = "";
        }

        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("_\\d+_" + ext + "$");
        java.util.regex.Matcher matcher = pattern.matcher(name);
        int start = -1;
        int end = -1;
        while (matcher.find()) {
            start = matcher.start();
            end = matcher.end();
        }
        StringBuilder sb = new StringBuilder();

        if (start > -1) {
            sb.append(name.substring(0, start)).append("_" + idx + "_").append(ext);
        } else {
            sb.append(nameOnly).append("_" + idx + "_").append(ext);
        }

        return sb.toString();
    }

    public int getRestoreCount() {
        return restoreCount;
    }
}
